#include <stdio.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <string.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <sys/stat.h>


void do_download(int sockfd,struct sockaddr_in serveraddr)
{
     char filename[128]="";
     printf("Please input the name of file you want to download:\n");
     scanf("%s",filename);
    
     unsigned char  text[1024] = "";
     int text_len;
     socklen_t addrlen = sizeof(serveraddr);
     int fd;
     int flags = 0;
     int num;
     ssize_t bytes;



     //built the instructions of TFTP and send it to server.
     text_len = sprintf(text,"%c%c%s%c%s%c",0,1,filename,0,"octet",0);
     if(sendto(sockfd,text ,text_len,0,(struct sockaddr *)&serveraddr,addrlen)<0)
     {
        perror("fail to sendto");
        exit(1);
     }

     while(1)
     {
          //Receive message from server
         if((bytes = recvfrom(sockfd,text,sizeof(text),0,(struct sockaddr *)&serveraddr,&addrlen))<0)
         {
            perror("fail to recvfrom");
            exit(1);
         }
         printf("OPcode from server is:%d,Block num:%u\n",text[1],ntohs(*(unsigned short *)(text +2)));
         printf("data:%s\n",text + 4);


         //Process errors
         if(text[1] == 5)//code from server is 5
         {
             printf("error: %s\n",text+4);
             break;
         }
         //Receive data
         else if(text[1] == 3)//code 3 is data package
         {
            if(flags == 0)
            {
                //Build file,open file with write ,if its not eixst ,then create it,if its already there open and clean it 
                if((fd = open(filename,O_WRONLY | O_CREAT | O_TRUNC,0664))<0)
                {
                    perror("fail to open");
                    exit(1);
                }
                flags = 1;
            }

            //After receive,write the data in the file.
            //if((num+1 == ntohs(*(unsigned short *)(text+2))) && (bytes == 516))
            if(num+1 == ntohs(*(unsigned short *)(text + 2)) &&( sizeof(text)== 516))
            {
                num = ntohs(*(unsigned short *)(text + 2));
                if(write(fd,text+4,bytes - 4)<0)
                {
                    perror("fail ot write");
                    exit(1);
                }

                //if finish write,send ACK to server
                text[1]=4;
                if(sendto(sockfd,text,4,0,(struct sockaddr *)&serveraddr,addrlen)<0)
                {
                    perror("fail to send");
                    exit(1);
                }
            }
            //when finish recive,break the loop
            else if(num+1 == ntohs(*(unsigned short *)(text + 2)) &&( sizeof(text)< 516))
            {
                num = ntohs(*(unsigned short *)(text + 2));
                if(write(fd,text+4,bytes - 4)<0)
                {
                    perror("fail ot write");
                    exit(1);
                }

                //if finish write,send ACK to server
                text[1]=4;
                if(sendto(sockfd,text,4,0,(struct sockaddr *)&serveraddr,addrlen)<0)
                {
                    perror("fail to send");
                    exit(1);
                } 
                printf("Finish download!!\n");
                break;
            }
         }
     }
}

int main(int argc,char const *argv[]){
    if(argc<2)
    {
        fprintf(stderr,"Usage:%s ip port\n",argv[0]);
        exit(1);
    }

     int sockfd;
    if((sockfd=socket(AF_INET,SOCK_DGRAM,0))==-1)
    {
        perror("fail to socket\n");
        exit(1);     
    }

     printf("sockfd=%d\n",sockfd);

     struct sockaddr_in serveraddr;
     socklen_t addrlen = sizeof(serveraddr);
     serveraddr.sin_family=AF_INET;
     serveraddr.sin_addr.s_addr=inet_addr(argv[1]);
     serveraddr.sin_port=htons(69);

    do_download(sockfd,serveraddr);

    return 0;
}